﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.IO;
using System.Data;
using System.Data.SqlClient;
using System.Windows.Forms;

namespace KengeeIT_ShopApp
{

    public class ClassFile
    {
        private SqlConnection _SqlConnection;
        public string MD5 = string.Empty;
        public string Version = string.Empty;
        public string FileName = string.Empty;
        public string FileSaveName = string.Empty;
        public string ProjectName = string.Empty;
        public long FileSize = -1;
        public int SplitFileSize = 300 * 1024;

        public ClassFile(string aProjectName, SqlConnection aSqlConnection)
        {
            _SqlConnection = aSqlConnection;
            this.ProjectName = aProjectName;
        }

        public ClassFile(string aFileName, string aProjectName, SqlConnection aSqlConnection)
        {
            _SqlConnection = aSqlConnection;
            this.FileName = aFileName;
            this.ProjectName = aProjectName;            
            this.FileSaveName = Path.GetFileName(aFileName);

            if (File.Exists(aFileName))
            {
                this.MD5 = GetFileMD5(FileName);

                FileVersionInfo fvi = FileVersionInfo.GetVersionInfo(FileName);
                this.Version = fvi.FileVersion;

                FileInfo fi = new FileInfo(FileName);
                this.FileSize = fi.Length;
            }
        }

        private string GetFileMD5(string aFileName)
        {
            string result = string.Empty;
            try
            {
                FileStream get_file = new FileStream(aFileName, FileMode.Open, FileAccess.Read, FileShare.Read);
                System.Security.Cryptography.MD5CryptoServiceProvider get_md5 = new System.Security.Cryptography.MD5CryptoServiceProvider();
                byte[] hash_byte = get_md5.ComputeHash(get_file);
                result = System.BitConverter.ToString(hash_byte);
                result = result.Replace("-", "");
                get_file.Dispose();
            }
            catch (Exception ex)
            {
                MessageBox.Show("function=ClassFile.GetFileMD5;\r\naFileName=" + aFileName + ";\r\n" + ex.ToString());
            }
            return result;
        }

        private int UpLoadFile_Small()
        {
            int result = -1;
            string sql = @"INSERT INTO [kengee_it_mis].[dbo].[ProgramFile]
                                       ([ProjectName]
                                       ,[FileName]
                                       ,[MD5]
                                       ,[Version]
                                       ,[FileContent])
                                 VALUES
                                       ('" + this.ProjectName + @"'
                                       ,'" + this.FileSaveName.Replace("'", "") + @"'
                                       ,'" + this.MD5 + @"'
                                       ,'" + this.Version + @"'
                                       ,@FileContent)"; //FileContent字段是Image类型
            try
            {
                byte[] FileBuffer;
                FileBuffer = File.ReadAllBytes(this.FileName); //读取文件内容

                SqlCommand cmd = _SqlConnection.CreateCommand();
                cmd.CommandText = sql;
                cmd.Parameters.Add("@FileContent", SqlDbType.Image);
                cmd.Parameters["@FileContent"].Value = FileBuffer; //接受byte[]类型的值
                _SqlConnection.Open();
                result = cmd.ExecuteNonQuery();
                _SqlConnection.Close();
            }
            catch (Exception ex)
            {
                MessageBox.Show("function=ClassFile.UpLoadFile_Small;\r\nSQL=" + sql + ";\r\n" + ex.ToString());
            }
            finally
            {
                _SqlConnection.Close();
            }
            return result;
        }

        public int DeleteDBFile()
        {
            int result = -1;
            string sql = "delete ProgramFile where (FileName='" + this.FileSaveName.Replace("'", "") + "') and (ProjectName='" + this.ProjectName + "')";
            try
            {

                SqlCommand scm = new SqlCommand(sql, _SqlConnection);
                scm.CommandText = sql;
                _SqlConnection.Open();
                result = scm.ExecuteNonQuery();
                _SqlConnection.Close();
            }
            catch (Exception ex)
            {
                MessageBox.Show("function=ClassFile.DeleteDBFile;\r\nsql=" + sql + ";\r\n" + ex.ToString());
            }
            finally
            {
                _SqlConnection.Close();
            }
            return result;
        }

        private int UpLoadFile_Big()
        {
            int result = 0;
            FileStream SplitFileStream = new FileStream(this.FileName, FileMode.Open);
            //以文件的全路对应的字符串和文件打开模式来初始化FileStream文件流实例

            BinaryReader SplitFileReader = new BinaryReader(SplitFileStream);
            //以FileStream文件流来初始化BinaryReader文件阅读器


            int iFileCount = (int)(SplitFileStream.Length / this.SplitFileSize);

            if (SplitFileStream.Length % this.SplitFileSize != 0)
            {
                iFileCount++;
            }
            //string[] TempExtra = ofd.FileNames[i].Split('.');

            /* 循环将大文件分割成多个小文件 */
            for (int i = 0; i < iFileCount; i++)
            {
                string sTempFileName = this.FileName + @"." + i.ToString().PadLeft(3, '0'); //小文件名
                //确定小文件的文件名称    　　　
                sTempFileName = sTempFileName.Replace("'", "");
                CreateSplitFile(SplitFileReader, sTempFileName);//生成分割文件

                UploadSplitFile(sTempFileName);//上传分割文件
            }
            SplitFileReader.Close();
            //关闭大文件阅读器
            SplitFileStream.Close();
            return result;
        }

        private bool CreateSplitFile(BinaryReader aFileReader, string aSplitFileName)
        {
            bool result = false;
            try
            {
                File.Delete(aSplitFileName);

                FileStream TempStream = new FileStream(aSplitFileName, FileMode.OpenOrCreate);
                //根据文件名称和文件打开模式来初始化FileStream文件流实例

                BinaryWriter TempWriter = new BinaryWriter(TempStream);
                //以FileStream实例来创建、初始化BinaryWriter书写器实例 

                byte[] TempBytes;//每次分割读取的最大数据

                TempBytes = aFileReader.ReadBytes(this.SplitFileSize);
                //从大文件中读取指定大小数据

                TempWriter.Write(TempBytes);
                //把此数据写入小文件

                TempWriter.Close();
                //关闭书写器，形成小文件

                TempStream.Close();
                //关闭文件流
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.ToString());
            }

            return result;
        }

        private int UploadSplitFile(string aSplitFileName)
        {
            int result = -1;
            ClassFile cfSplit = new ClassFile(aSplitFileName, this.ProjectName, this._SqlConnection);
            cfSplit.Version = cfSplit.FileSaveName;//分解文件中，version保存分解文件名；
            cfSplit.FileSaveName = this.FileSaveName;//分解文件中，filesavename保存原始文件名；
            cfSplit.UpLoadFile_Small();
            File.Delete(aSplitFileName);
            return result;
        }

        public int UpLoadFile()
        {
            int result = -1;
            this.DeleteDBFile();
            if (this.FileSize <= SplitFileSize)
            {
                //写入文件:文件大小<=500K
                result = this.UpLoadFile_Small();
            }
            else
            {
                //写入文件:文件大小>500K
                result = this.UpLoadFile_Big();
            }
            return result;
        }

        private bool DownloadFile_Small()
        {
            bool result = false;
            string sql=@"SELECT FileContent FROM ProgramFile where (FileName='" + this.FileSaveName + "') and (ProjectName='" + this.ProjectName + "')";
            try
            {
                while (result == false)
                {
                    byte[] buffer;
                    SqlCommand cmd = _SqlConnection.CreateCommand();
                    cmd.CommandText = sql;
                    _SqlConnection.Open();
                    buffer = (byte[])cmd.ExecuteScalar(); //从数据库中取到的Image类型的字段的值，转换成byte[]类型
                    _SqlConnection.Close();
                    File.WriteAllBytes(this.FileName, buffer);

                    string NewFileMD5 = GetFileMD5(this.FileName);
                    if (NewFileMD5.Equals(this.MD5))
                    {
                        result = true;
                    }
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show("function=ClassFile.DownloadFile_Small;\r\nsql=" + sql + ";\r\n" + ex.ToString());
            }
            finally
            {
                _SqlConnection.Close();
            }
            return result;
        }

        private string GetSplitFileMD5FormDB(string aSplitFileSaveName)
        {
            string result = string.Empty;
            string sql=@"SELECT MD5 FROM ProgramFile where (Version='" + aSplitFileSaveName + "') and (ProjectName='" + this.ProjectName + "')";               
            try
            {
                SqlCommand cmd = _SqlConnection.CreateCommand();
                cmd.CommandText = sql;
                _SqlConnection.Open();
                result = cmd.ExecuteScalar().ToString(); //从数据库中取到的Image类型的字段的值，转换成byte[]类型
                _SqlConnection.Close();
            }
            catch (Exception ex)
            {
                MessageBox.Show("function=ClassFile.GetSplitFileMD5FormDB;\r\nsql=" + sql + ";\r\n" + ex.ToString());
            }
            finally
            {
                _SqlConnection.Close();
            }
            return result;
        }

        private bool DownloadFile_Split(int aSplitCount)
        {
            bool result = false;
            string sql = string.Empty;
            try
            {
                FileStream fsMergeFile = new FileStream(this.FileName, FileMode.CreateNew, FileAccess.Write);
                BinaryWriter bwMergeFile = new BinaryWriter(fsMergeFile);

                for (int i = 0; i < aSplitCount; i++)
                {
                    //下载文件
                    string SplitFileSaveName = this.FileSaveName + @"." + i.ToString().PadLeft(3, '0');
                    string SplitFileName = this.FileName + @"." + i.ToString().PadLeft(3, '0');
                    string NewMD5 = string.Empty;

                    string DBMD5=GetSplitFileMD5FormDB(SplitFileSaveName);
                    while (NewMD5.Equals(DBMD5) == false)
                    {
                        File.Delete(SplitFileName);
                        byte[] buffer;
                        SqlCommand cmd = _SqlConnection.CreateCommand();
                        sql=@"SELECT FileContent FROM ProgramFile where (Version='" + SplitFileSaveName + "') and (ProjectName='" + this.ProjectName + "')";
                        cmd.CommandText = sql;
                        _SqlConnection.Open();
                        buffer = (byte[])cmd.ExecuteScalar(); //从数据库中取到的Image类型的字段的值，转换成byte[]类型
                        _SqlConnection.Close();
                        File.WriteAllBytes(SplitFileName, buffer);
                        NewMD5 = GetFileMD5(SplitFileName);
                    }

                    //合并文件
                    FileStream tempStream = new FileStream(SplitFileName, FileMode.Open, FileAccess.Read);
                    BinaryReader tempReader = new BinaryReader(tempStream);
                    byte[] data = tempReader.ReadBytes(this.SplitFileSize);
                    bwMergeFile.Write(data, 0, data.Length);
                    bwMergeFile.Flush();
                    tempReader.Close();
                    tempStream.Close();
                    File.Delete(SplitFileName);
                }
                bwMergeFile.Flush();
                bwMergeFile.Close();
                fsMergeFile.Close();
           }
            catch (Exception ex)
            {
                MessageBox.Show("function=ClassFile.DownloadFile_Split;\r\nsql=" + sql + ";\r\n" + ex.ToString());
            }
            finally
            {
                _SqlConnection.Close();
            }
            return result;
        }

        public bool DownloadFile()
        {
            bool result = false;
            string sql= @"select count(*) from ProgramFile where (ProjectName='" + this.ProjectName + "') and (FileName='" + this.FileSaveName + "')";
            try
            {
                File.Delete(this.FileName);
                SqlCommand scDownloadFile = _SqlConnection.CreateCommand();
                scDownloadFile.CommandText = sql;
                _SqlConnection.Open();
                int iCount = (int)scDownloadFile.ExecuteScalar();
                _SqlConnection.Close();

                if (iCount == 1)
                {
                    DownloadFile_Small();
                }
                else if (iCount > 1)
                {
                    DownloadFile_Split(iCount);
                }
                result = true;
            }
            catch (Exception ex)
            {
                MessageBox.Show("function=ClassFile.DownloadFile;\r\nsql=" + sql + ";\r\n" + ex.ToString());
            }
            finally
            {
                _SqlConnection.Close();
            }
            return result;
        }

        public DataTable GetFileList()
        {
            string sql = @"select ID,FileName,Version,MD5 from ProgramFile where ProjectName='" + this.ProjectName + "'";
            DataTable dtProgramFiles = new DataTable();
            try
            {
                SqlDataAdapter sdaProgramFiles = new SqlDataAdapter(sql, _SqlConnection);
                dtProgramFiles = new DataTable();
                _SqlConnection.Open();
                sdaProgramFiles.Fill(dtProgramFiles);
                _SqlConnection.Close();
                sdaProgramFiles.Dispose();
            }
            catch (Exception ex)
            {
                MessageBox.Show("function=ClassFile.GetFileList;\r\nsql=" + sql + ";\r\n" + ex.ToString());
            }
            finally
            {
                _SqlConnection.Close();
            }

            return dtProgramFiles;
        }

        public bool AutoUpdate(string aArgument)
        {
            bool result = false;
            DataTable dtProgramFiles = GetFileList();
            try
            {
                string CurrentPath = Path.GetDirectoryName(Application.ExecutablePath) + "\\";
                for (int i = 0; i < dtProgramFiles.Rows.Count; i++)
                {
                    string DBFileName = dtProgramFiles.Rows[i]["FileName"].ToString();
                    string DBVersion = dtProgramFiles.Rows[i]["Version"].ToString();
                    string DBMD5 = dtProgramFiles.Rows[i]["MD5"].ToString();
                    string FilePath = CurrentPath + DBFileName;
                    if (File.Exists(FilePath))
                    {
                        FileVersionInfo fvi = FileVersionInfo.GetVersionInfo(FilePath);
                        if (fvi.FileVersion.Equals(DBVersion) == false)
                        {
                            string MD5 = string.Empty;
                            while (MD5 != DBMD5)
                            {
                                File.Delete(FilePath);
                                ClassFile cf = new ClassFile(DBFileName, this.ProjectName, this._SqlConnection);
                                cf.DownloadFile();
                                MD5 = this.GetFileMD5(FilePath);
                            }
                            string ExtName = Path.GetExtension(FilePath);
                            if (ExtName.Equals(".upp"))
                            {
                                Process p = new Process();
                                p.StartInfo.FileName = CurrentPath + "UpdateProgram.exe";
                                p.StartInfo.Arguments = aArgument;
                                p.Start(); //启动
                                Application.Exit();
                            }
                        }
                    }
                    else
                    {
                        string MD5 = string.Empty;
                        while (MD5 != DBMD5)
                        {
                            ClassFile cf = new ClassFile(DBFileName, this.ProjectName, this._SqlConnection);
                            cf.DownloadFile();
                            MD5 = this.GetFileMD5(FilePath);
                        }
                    }
                }
                result = true;
            }
            catch (Exception ex)
            {
                MessageBox.Show("function=ClassFile.AutoUpdate;\r\n" + ex.ToString());
            }
            return result;
        }
    }
}
